package com.world.rickcloudendpoint.service.tools;

import cn.hutool.core.lang.Assert;
import com.world.rickcloudendpoint.common.constant.DetailErrorCodes;
import com.world.rickcloudendpoint.common.constant.GlobalConstants;
import com.world.rickcloudendpoint.common.exception.ModuleException;
import com.world.rickcloudendpoint.models.params.common.QueryDsl;
import com.world.rickcloudendpoint.service.uuid.LongIdGenerator;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * <b>
 * 具体的实现思路是这样的，就是逐个的消除表达式中的括号，直到表达式中不包含圆括号，就用上面的表达式来举个例子，((a|b)&c)|((a&b&d)|e)
 * <p>
 * 　　1、找到最后一个“(”，之后寻找与之匹配的“)”，处理这对圆括号中的简单表达式，这里是a&b&d，处理完之后将结果放在一个字典之中<guid,filter>，记作<1,filter1>，之后字符串变为((a|b)&c)|(1|e)
 * <p>
 * 　　2、参照1的顺序再次处理表达式((a|b)&c)|(1|e)，这次处理1|e，字典中添加一项<2,filter2>，字符串变为((a|b)&c)|2
 * <p>
 * 　　3、处理a|b，字典中添加一项<3,filter3>，字符串变为(3&c)|2
 * <p>
 * 　　4、处理3&c，字典中添加一项<4,filter4>，字符串变为4|2
 * <p>
 * 　　5、至此，圆括号已不再，只是简单的表达式，这就简单了
 * </b>
 *
 * @description: mongodb通用查询表达式解析工具
 * @author: jlsong
 * @date: 2022/01/09 17:19:58
 * @version: 1.0
 */
public class MongoDslDecoderTools {

    /**
     * 临时存储拆解的表达式
     */
    private static final Map<String, Criteria> tempCache = new HashMap<>(16);


    /**
     * 通用查询表达式
     *
     * @param exp  表达式
     * @param dict 查询因子
     * @return
     */
    public static Query getMongoQuery(String exp, Map<String, QueryDsl> dict) throws ModuleException {
        tempCache.clear();
        Query query = new Query();
        Criteria criteria = decoderExpBrackets(exp, dict);
        if (criteria != null) {
            query.addCriteria(criteria);
        }
        return query;
    }

    /**
     * 解析表达式中的括号
     *
     * @param logicExp 表达式
     * @param dict     查询因子
     */
    private static Criteria decoderExpBrackets(String logicExp, Map<String, QueryDsl> dict) throws ModuleException {
        int startIndex = logicExp.lastIndexOf("(");
        if (startIndex != -1) {
            // 截取括号中的表达式
            int endIndex = logicExp.indexOf(")", startIndex);
            int expLen = endIndex - startIndex - 1;
            String simpleExp = logicExp.substring(startIndex + 1, expLen);
            // 处理简单的表达式并结果保存到字典中
            String tempId = LongIdGenerator.generateId() + "";
            Criteria criteria = decoderSimpleExp(simpleExp, dict);
            if(criteria != null){
                tempCache.put(tempId, criteria);
            }
            // 继续处理剩余表达式
            String leftStr = logicExp.substring(0, startIndex);
            String rightStr = logicExp.substring(endIndex + 1);
            return decoderExpBrackets(String.format("%s%s%s", leftStr, tempId, rightStr), dict);
        }
        return decoderSimpleExp(logicExp, dict);
    }

    /**
     * 处理简单的逻辑表达式(不包括圆括号)
     *
     * @param simpleExp 简单逻辑表达式
     * @param dict      过滤因子
     * @return
     */
    private static Criteria decoderSimpleExp(String simpleExp, Map<String, QueryDsl> dict) throws ModuleException {
        // 1、刷选出操作符：&、|
        LinkedBlockingQueue<Character> opQueue = new LinkedBlockingQueue();
        for (char c : simpleExp.toCharArray()) {
            opQueue.offer(c);
        }

        // 2、刷选出所有的变量
        LinkedBlockingQueue<String> varQueue = new LinkedBlockingQueue<>();
        String[] tempVar = simpleExp.replace(GlobalConstants.Mongodb.AND_SYMBOL, GlobalConstants.Mongodb.SPLIT_SYMBOL)
                .replace(GlobalConstants.Mongodb.OR_SYMBOL, GlobalConstants.Mongodb.SPLIT_SYMBOL)
                .split(String.valueOf(GlobalConstants.Mongodb.SPLIT_SYMBOL));
        for (String var : tempVar) {
            varQueue.offer(var);
        }

        // 3、返回结果组装
        Criteria criteria = null;
        if (varQueue.size() >= 1) {
            String var = varQueue.poll();
            criteria = tempCache.get(var) != null ? tempCache.get(var) : decoderExpFactor(dict.get(var));

            while (criteria != null && varQueue.size() > 0) {
                String rightVar = varQueue.poll();
                Criteria tempCriteria = tempCache.get(rightVar) != null ? tempCache.get(rightVar) : decoderExpFactor(dict.get(rightVar));
                char tempOp = opQueue.poll();
                switch (tempOp) {
                    case GlobalConstants.Mongodb.AND_SYMBOL:
                        criteria.andOperator(tempCriteria);
                        break;
                    case GlobalConstants.Mongodb.OR_SYMBOL:
                        criteria.orOperator(tempCriteria);
                        break;
                }
            }
        }
        return criteria;

    }

    /**
     * 将查询因子转换成Mongodb的Filter
     *
     * @param dsl 查询因子
     * @return
     */
    private static Criteria decoderExpFactor(QueryDsl dsl) throws ModuleException {
        Assert.notNull(dsl, "查询因子dsl为空");
        Assert.notBlank(dsl.getField(), "查询因子dsl的field为空值");
        Assert.notBlank(dsl.getOp(), "查询因子dsl的op为空值");
        Assert.notNull(dsl.getValue(), "查询因子dsl的value为空值");
        Criteria criteria = null;
        fieldTransform(dsl);
        switch (dsl.getOp().toLowerCase()) {
            case "ne":
                criteria = Criteria.where(dsl.getField()).ne(dsl.getValue());
                break;
            case "gt":
                criteria = Criteria.where(dsl.getField()).gt(dsl.getValue());
                break;
            case "gte":
                criteria = Criteria.where(dsl.getField()).gte(dsl.getValue());
                break;
            case "lt":
                criteria = Criteria.where(dsl.getField()).lt(dsl.getValue());
                break;
            case "lte":
                criteria = Criteria.where(dsl.getField()).lte(dsl.getValue());
                break;
            case "like":
                criteria = Criteria.where(dsl.getField()).regex(String.format(".*?%s.*", dsl.getValue()));
                break;
            case "eq":
                criteria = Criteria.where(dsl.getField()).is(dsl.getValue());
                break;
            default:
                throw new ModuleException(DetailErrorCodes.Object_Is_Null.value(), "操作符号'".concat(dsl.getOp()).concat("'不识别"));
        }
        return criteria;
    }

    private static void fieldTransform(QueryDsl dsl) {
        if (dsl == null) {
            return;
        }
        if (dsl.getField().equals("id")) {
            dsl.setField("_id");
            dsl.setValue(new ObjectId(dsl.getValue().toString()));
        }
    }


}
